/*
 * shaderPool.cpp
 *
 *  Created on: Oct 14, 2012
 *      Author: travis 
 */

#ifdef _WIN32 
#include <windows.h> 
#endif

#include "ShaderPool.h"

namespace Shader
{

unsigned int runningProgram = 0;

ShaderPool::ShaderPool()
{
	files = std::vector<shaderInfo>();
}

ShaderPool::~ShaderPool()
{
	// call clean function
	clean();
}

// load file
// input filename
// output loaded string form file
int ShaderPool::loadFile(const char* fn, std::string& str)
{
	std::ifstream in(fn);
	if (!in.is_open())
	{
		std::string filename = fn;

		throw new Engine::RTS_EXCEPTION(type, fn, ERR_FILE_NOT_FOUND);
		std::cerr << "SHADERS: Cannot load file: " << fn << std::endl;
		return 1;
	}

	char tmp[400];
	while (!in.eof())
	{
		in.getline(tmp, 400);
		str += tmp;
		str += '\n';
	}

	return 0;
}

// compile shader source
// return id in memory
GLuint ShaderPool::loadShader(std::string& source, GLuint mode)
{
	GLuint id;
	id = glCreateShader(mode);

	const char* csource = source.c_str();
	int len = strlen(csource);
	glShaderSource(id, 1, &csource, &len);

	glCompileShader(id);

	char error[1000];

	glGetShaderInfoLog(id, 1000, NULL, error);
#ifdef _DEBUG
	std::cout << "SHADERS: Compile status: " << error << std::endl;
#endif
	return id;
}

// initialization of new shader
// vname - filename of vertex shader
// fnema - filename of fragment shader
// pname - name of program
int ShaderPool::initShader(std::string vname, std::string fname,
		std::string pname)
{
	std::string source;
	GLuint vs, fs, program;
	shaderInfo shibuff;
	std::string vnamebuff, fnamebuff;

	// vertex
#ifdef _DEBUG
	std::cout << "\nSHADERS: Loading vertex shader for " << pname << std::endl;
#endif
	vnamebuff = Engine::fileStructure.shaderPath.c_str() + pname + '/' + vname;

	loadFile(vnamebuff.c_str(), source);

	vs = loadShader(source, GL_VERTEX_SHADER);
	//std::cout << source << std::endl;
	source = "";

	// fragment
#ifdef _DEBUG
	std::cout << "SHADERS: Loading fragment shader for " << pname << std::endl;
#endif

	fnamebuff = Engine::fileStructure.shaderPath.c_str() + pname + '/' + fname;

	loadFile(fnamebuff.c_str(), source);

	fs = loadShader(source, GL_FRAGMENT_SHADER);

	program = glCreateProgram();
	glAttachShader(program, vs);
	glAttachShader(program, fs);

	glLinkProgram(program);

	shibuff.fname = pname;
	shibuff.program = program;
	shibuff.vs = vs;
	shibuff.fs = fs;

	// insert program to the pool
	files.push_back(shibuff);

#ifdef _DEBUG
	std::cout << "\nSHADERS: Shader " << pname << " info:\n" << "  Program: " << program
			<< "\n  VS: " << vs << "\n  FS: " << fs << std::endl;
#endif

	return program;
}

void ShaderPool::clean()
{
#ifdef _DEBUG
	std::cout << "SHADERS: Deleting shaders" << std::endl;
#endif
	for (std::vector<shaderInfo>::iterator it = files.begin(); it != files.end();
			++it)
	{
		glDetachShader(it->program, it->vs);
		glDetachShader(it->program, it->fs);

		glDeleteShader(it->vs);
		glDeleteShader(it->fs);

		glDeleteProgram(it->program);
	}
}

// return program ID stored in vector of programs
// filename is name of folder with fragment and vertex shader
GLuint ShaderPool::getProgramID(std::string filename)
{
	for (std::vector<shaderInfo>::iterator it = files.begin(); it != files.end();
			++it)
	{
		if (it->fname.compare(filename) == 0)
		{
			return it->program;
		}
	}

	int pID = initShader("vertex.vsh", "fragment.fsh", filename);

	if ((pID) == -1)
	{
		std::cerr << "SHADERS: Shader program " << filename << " not found." << std::endl;
		return 100;
	}

	return pID;
}

// return ID of vertex shader
// when error returns 100
GLuint ShaderPool::getVSID(std::string filename)
{
	for (std::vector<shaderInfo>::iterator it = files.begin(); it != files.end();
			++it)
	{
		if (it->fname.compare(filename) == 0)
		{
			return it->vs;
		}
	}

	std::cerr << "SHADERS: Shader program " << filename << " not found." << std::endl;

	return 100;
}

// return ID of fragment shader
// when error returns 100
GLuint ShaderPool::getFSID(std::string filename)
{
	for (std::vector<shaderInfo>::iterator it = files.begin(); it != files.end();
			++it)
	{
		if (it->fname.compare(filename) == 0)
		{
			return it->fs;
		}
	}

	std::cerr << "SHADERS: Shader program " << filename << " not found." << std::endl;

	return 100;
}

// use shader program
// input filename, name of directory with vertex
// and fragment shader
void ShaderPool::useProgram(std::string filename)
{
	glUseProgram(getProgramID(filename));
}

// load whole directory with shaders
int ShaderPool::loadDir()
{
	std::string buf;

	DIR *dp;
	struct dirent *dirp;

	if ((dp = opendir(Engine::fileStructure.shaderPath.c_str())) == NULL)
	{
		std::cerr << "SHADERS: Error opening " << Engine::fileStructure.shaderPath
				<< std::endl;

		return 1;
	}

#ifdef _DEBUG
	std::cout << "\nSHADERS: Files in " << Engine::fileStructure.shaderPath << ": "
			<< std::endl;
#endif

	while ((dirp = readdir(dp)) != NULL)
	{
		buf = dirp->d_name;

		if (buf.compare(".") && buf.compare(".."))
		{
#ifdef _DEBUG
			std::cout << "SHADERS: Found shader program: " << dirp->d_name << std::endl;
#endif
			initShader("vertex.vs", "fragment.frag", dirp->d_name);

			//loadImage(dirp->d_name);
		}
	}
	closedir(dp);
	return 0;
}

// return vector with shaders
std::vector<shaderInfo> ShaderPool::getShaders()
{
	return files;
}

// return count of programs
int ShaderPool::getCount()
{
	return files.size();
}

ShaderPool *sPool;

void initShaderPool()
{
	sPool = new ShaderPool();
}

void deleteShadePool()
{
	delete (sPool);
}
}

